#ifndef _QUANTITY_H_
#define _QUANTITY_H_

#include <Unit.h>
#include <stdio.h>
#include <stdexcept> 
using namespace std;

#define PRINT_MODE0 0
#define PRINT_MODE1 1

template <class T>
class Quantity 
{
public:
    typedef Quantity<T> Self;
    Quantity(const unsigned int amount, const T &unit);
    bool operator==(const Self &other) const;
    bool operator!=(const Self &other) const;
    Self operator +(const Self &other) const;
    string print(unsigned int printMode = 0) const;
private:
    string printDirectly(bool & isPreviousUnitPrinted, const bool isForcePrint = false) const;
    unsigned int calcValueInBasicUnit() const;
    unsigned int calcUnitFactor(const Self & other) const;
    const T &unit;
    unsigned int amount;
};


template <class T>
Quantity<T>::Quantity(const unsigned int amount, const T  &unit)
:amount(amount), unit(unit)
{
}

template <class T>
unsigned int Quantity<T>::calcValueInBasicUnit() const
{
        return amount * unit.getUnitCoefficient();
}

template <class T>
unsigned int Quantity<T>::calcUnitFactor(const  Quantity<T>  & other) const
{
	if (unit.getUnitCoefficient() > other.unit.getUnitCoefficient())
	{
		return unit.getUnitCoefficient() / other.unit.getUnitCoefficient();
	}
	else
	{
		return other.unit.getUnitCoefficient() / unit.getUnitCoefficient();
	}
}

template <class T>
bool Quantity<T>::operator==(const Quantity<T> &other) const
{
        return calcValueInBasicUnit() == other.calcValueInBasicUnit();
}

template <class T>
bool Quantity<T>::operator!=(const Quantity<T> &other) const
{
    return !operator==(other);
}

template <class T>
Quantity<T> Quantity<T>::operator+(const Quantity<T> &other) const
{
    unsigned int amount;
    unsigned int factor = calcUnitFactor(other);
    if (other.unit > this->unit)
    {
        amount = this->amount + other.amount * factor;
        return Quantity<T>(amount, this->unit);
    }
    else
    {
        amount = this->amount * factor + other.amount;
        return Quantity<T>(amount,other.unit);
    }
}

//print to string directly
template <class T>
string Quantity<T>::printDirectly(bool & isPreviousUnitPrinted, const bool isForcePrint) const
{
	if ((0 == amount) && (!isForcePrint))
	{
		isPreviousUnitPrinted = false;
		return "";
	}
	isPreviousUnitPrinted = true;
	char   buf[12] = {0};
	sprintf(buf,"%d",amount);
	string str(buf);
	str += " ";
	return str+ unit.getUnitName();
}

//print to string according predifined format
template <class T>
string Quantity<T>::print(unsigned int printMode) const 
{
	if (printMode >= MAX_UNIT_PRINT_MODE)
	{
		throw std::invalid_argument("INVAID_UNIT_PRINT_MODE");
	}
	const T** pUnitTab = T::unitMgr.getUnitTab();
	const unsigned int numberOfUnit = T::unitMgr.getNumberOfUnit();
	bool isPreviousUnitPrinted;
	if (0 == amount)
	{
		Quantity<T>  remain(0,*pUnitTab[numberOfUnit - 1]);
		return remain.printDirectly(isPreviousUnitPrinted, true);
	}
	Quantity<T> remain(*this);
        string result;
	for (int i = 0; i < numberOfUnit; i++)
	{
		if ( !pUnitTab[i]->getPrintFlag(printMode) )
		{
			continue;
		}
		Quantity<T>  reduction( remain.calcValueInBasicUnit() / pUnitTab[i]->getUnitCoefficient(), *pUnitTab[i]);
		result += reduction.printDirectly(isPreviousUnitPrinted);
		if ((remain.unit > *pUnitTab[i]) || (0 == remain.amount))
		{
			break;
		}
		unsigned int factor = pUnitTab[i]->getUnitCoefficient()/unit.getUnitCoefficient(); 
		remain.amount =  remain.amount  % factor;
		if ( isPreviousUnitPrinted && remain.amount && (pUnitTab[i + 1]->getPrintFlag(printMode)))
		{
			result += " ";
		}
	}
	return result;
}
#endif
